package org.xtext.runtime.plan;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.xtext.runtime.interpreter.Agent;
import org.xtext.runtime.term.Variable;
import org.xtext.runtime.util.Bindings;

public class SynchronizedBlockStep implements IPlanStep {
	String token;
	BlockStep step;
	int state = 0;
	
	public SynchronizedBlockStep(String token, BlockStep step) {
		this.token = token;
		this.step = step;
	}

	@Override
	public IPlanStep apply(Bindings set) {
		return new SynchronizedBlockStep(token, step.apply(set));
	}

	@Override
	public void handle(Agent agent, PlanStack planStack) {
//		System.out.println("[SynchronizedBlockStep] State: " + state);
		if (state == 0 && agent.hasLock(token, planStack)) {
			// Here, the agent already has the lock for the token, so we treat
			// the synchronized block as a normal block...
			planStack.addPlanStep(step);
		} else {
			// Here the agent does not have a lock, so it must request the lock
			// perform the steps, and then release the lock...
			switch (state) {
			case 0:
				state++;
				if (!agent.requestLock(token, planStack)) {
					break;
				}
			case 1:
				state++;
				planStack.back();
				planStack.addPlanStep(step);
				break;
			case 2:
				agent.releaseLock(token, planStack);
			}
		}
	}

	public String toString() {
		return toString("");
	}
	@Override
	public String toString(String tabs) {
		return tabs + "synchronized (" + token + ") {...}";
	}

	public String toFullString(String tabs) {
		return "synchronized (" + token + ") " + step.toFullString(tabs);
	}
	
	@Override
	public Set<Variable> variables() {
		Set<Variable> variables = new HashSet<Variable>();
		variables.addAll(step.variables());
		return variables;
	}

	@Override
	public IPlanStep copy(Map<String, Variable> vars) {
		return new SynchronizedBlockStep(token, step.copy(vars));
	}

	@Override
	public void reset() {
	}
}
